home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / ufw / common.py < prev    next >
Text File  |  2009-09-23  |  14KB  |  440 lines

  1. #
  2. # common.py: common classes for ufw
  3. #
  4. # Copyright 2008-2009 Canonical Ltd.
  5. #
  6. #    This program is free software: you can redistribute it and/or modify
  7. #    it under the terms of the GNU General Public License version 3,
  8. #    as published by the Free Software Foundation.
  9. #
  10. #    This program is distributed in the hope that it will be useful,
  11. #    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. #    GNU General Public License for more details.
  14. #
  15. #    You should have received a copy of the GNU General Public License
  16. #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17. #
  18.  
  19. import re
  20. import socket
  21. import ufw.util
  22. from ufw.util import debug
  23.  
  24. programName = "ufw"
  25. state_dir = "/lib/ufw"
  26. trans_dir = "/usr/share/ufw"
  27. config_dir = "/etc"
  28. prefix_dir = "/usr"
  29. iptables_dir = "/sbin"
  30.  
  31. class UFWError(Exception):
  32.     '''This class represents ufw exceptions'''
  33.     def __init__(self, value):
  34.         self.value = value
  35.  
  36.     def __str__(self):
  37.         return repr(self.value)
  38.  
  39.  
  40. class UFWRule:
  41.     '''This class represents firewall rules'''
  42.     def __init__(self, action, protocol, dport="any", dst="0.0.0.0/0",
  43.                  sport="any", src="0.0.0.0/0", direction="in"):
  44.         # Be sure to update dup_rule accordingly...
  45.         self.remove = False
  46.         self.updated = False
  47.         self.v6 = False
  48.         self.dst = ""
  49.         self.src = ""
  50.         self.dport = ""
  51.         self.sport = ""
  52.         self.protocol = ""
  53.         self.multi = False
  54.         self.dapp = ""
  55.         self.sapp = ""
  56.         self.action = ""
  57.         self.position = 0
  58.         self.logtype = ""
  59.         self.interface_in = ""
  60.         self.interface_out = ""
  61.         try:
  62.             self.set_action(action)
  63.             self.set_protocol(protocol)
  64.             self.set_port(dport)
  65.             self.set_port(sport, "src")
  66.             self.set_src(src)
  67.             self.set_dst(dst)
  68.             self.set_direction(direction)
  69.         except UFWError:
  70.             raise
  71.  
  72.     def __str__(self):
  73.         return self.format_rule()
  74.  
  75.     def dup_rule(self):
  76.         '''Return a duplicate of a rule'''
  77.         rule = UFWRule(self.action, self.protocol)
  78.         rule.remove = self.remove
  79.         rule.updated = self.updated
  80.         rule.v6 = self.v6
  81.         rule.dst = self.dst
  82.         rule.src = self.src
  83.         rule.dport = self.dport
  84.         rule.sport = self.sport
  85.         rule.multi = self.multi
  86.         rule.dapp = self.dapp
  87.         rule.sapp = self.sapp
  88.         rule.position = self.position
  89.         rule.logtype = self.logtype
  90.         rule.interface_in = self.interface_in
  91.         rule.interface_out = self.interface_out
  92.         rule.direction = self.direction
  93.  
  94.         return rule
  95.  
  96.     def format_rule(self):
  97.         '''Format rule for later parsing'''
  98.         str = ""
  99.  
  100.         if self.interface_in != "":
  101.             str += " -i %s" % (self.interface_in)
  102.         if self.interface_out != "":
  103.             str += " -o %s" % (self.interface_out)
  104.  
  105.         # Protocol is handled below
  106.         if self.protocol == "any":
  107.             str += " -p all"
  108.         else:
  109.             str += " -p " + self.protocol
  110.  
  111.             if self.multi:
  112.                 str += " -m multiport"
  113.                 if self.dport != "any" and self.sport != "any":
  114.                     str += " --dports " + self.dport
  115.                     str += " -m multiport"
  116.                     str += " --sports " + self.sport
  117.                 elif self.dport != "any":
  118.                     str += " --dports " + self.dport
  119.                 elif self.sport != "any":
  120.                     str += " --sports " + self.sport
  121.  
  122.         if self.dst != "0.0.0.0/0" and self.dst != "::/0":
  123.             str += " -d " + self.dst
  124.         if not self.multi and self.dport != "any":
  125.             str += " --dport " + self.dport
  126.         if self.src != "0.0.0.0/0" and self.src != "::/0":
  127.             str += " -s " + self.src
  128.         if not self.multi and self.sport != "any":
  129.             str += " --sport " + self.sport
  130.  
  131.         lstr = ""
  132.         if self.logtype != "":
  133.             lstr = "_" + self.logtype
  134.         if self.action == "allow":
  135.             str += " -j ACCEPT%s" % (lstr)
  136.         elif self.action == "reject":
  137.             str += " -j REJECT%s" % (lstr)
  138.             if self.protocol == "tcp":
  139.                 # follow TCP's default and send RST
  140.                 str += " --reject-with tcp-reset"
  141.         elif self.action == "limit":
  142.             # Caller needs to change this
  143.             str += " -j LIMIT%s" % (lstr)
  144.         else:
  145.             str += " -j DROP%s" % (lstr)
  146.  
  147.         if self.dapp != "" or self.sapp != "":
  148.             # Format the comment string, and quote it just in case
  149.             comment = "-m comment --comment '"
  150.             pat_space = re.compile(' ')
  151.             if self.dapp != "":
  152.                 comment += "dapp_" + pat_space.sub('%20', self.dapp)
  153.             if self.dapp != "" and self.sapp != "":
  154.                 comment += ","
  155.             if self.sapp != "":
  156.                 comment += "sapp_" + pat_space.sub('%20', self.sapp)
  157.             comment += "'"
  158.  
  159.             str += " " + comment
  160.  
  161.         return str.strip()
  162.  
  163.     def set_action(self, action):
  164.         '''Sets action of the rule'''
  165.         tmp = action.lower().split('_')
  166.         if tmp[0] == "allow" or tmp[0] == "reject" or tmp[0] == "limit":
  167.             self.action = tmp[0]
  168.         else:
  169.             self.action = "deny"
  170.  
  171.         logtype = ""
  172.         if len(tmp) > 1:
  173.              logtype = tmp[1]
  174.         self.set_logtype(logtype)
  175.  
  176.     def set_port(self, port, loc="dst"):
  177.         '''Sets port and location (destination or source) of the rule'''
  178.         err_msg = _("Bad port '%s'") % (port)
  179.         if port == "any":
  180.             pass
  181.         elif loc == "dst" and self.dapp:
  182.             pass
  183.         elif loc == "src" and self.sapp:
  184.             pass
  185.         elif re.match(r'^[,:]', port) or re.match(r'[,:]$', port):
  186.             raise UFWError(err_msg)
  187.         elif (port.count(',') + port.count(':')) > 14:
  188.             # Limitation of iptables
  189.             raise UFWError(err_msg)
  190.         else:
  191.             ports = port.split(',')
  192.             if len(ports) < 1:
  193.                 raise UFWError(err_msg)
  194.             elif len(ports) > 1:
  195.                 self.multi = True
  196.  
  197.             tmp = ""
  198.             for p in ports:
  199.                 if re.match(r'^\d+:\d+$', p):
  200.                     # Port range
  201.                     self.multi = True
  202.                     ran = p.split(':')
  203.                     if len(ran) != 2:
  204.                         raise UFWError(err_msg)
  205.                     for q in ran:
  206.                         if int(q) < 1 or int(q) > 65535:
  207.                             raise UFWError(err_msg)
  208.                     if int(ran[0]) >= int(ran[1]):
  209.                         raise UFWError(err_msg)
  210.                 elif re.match('^\d+$', p):
  211.                     if int(p) < 1 or int(p) > 65535:
  212.                         raise UFWError(err_msg)
  213.                 elif re.match(r'^\w[\w\-]+', p):
  214.                     try:
  215.                         p = socket.getservbyname(p)
  216.                     except Exception, (error):
  217.                         raise UFWError(err_msg)
  218.                 else:
  219.                     raise UFWError(err_msg)
  220.  
  221.                 if tmp:
  222.                     tmp += "," + str(p)
  223.                 else:
  224.                     tmp = str(p)
  225.  
  226.             port = tmp
  227.  
  228.         if loc == "src":
  229.             self.sport = str(port)
  230.         else:
  231.             self.dport = str(port)
  232.  
  233.     def set_protocol(self, protocol):
  234.         '''Sets protocol of the rule'''
  235.         if protocol == "tcp" or protocol == "udp" or protocol == "any":
  236.             self.protocol = protocol
  237.         else:
  238.             err_msg = _("Unsupported protocol '%s'") % (protocol)
  239.             raise UFWError(err_msg)
  240.  
  241.     def _fix_anywhere(self):
  242.         '''Adjusts src and dst based on v6'''
  243.         if self.v6:
  244.             if self.dst and (self.dst == "any" or self.dst == "0.0.0.0/0"):
  245.                 self.dst = "::/0"
  246.             if self.src and (self.src == "any" or self.src == "0.0.0.0/0"):
  247.                 self.src = "::/0"
  248.         else:
  249.             if self.dst and (self.dst == "any" or self.dst == "::/0"):
  250.                 self.dst = "0.0.0.0/0"
  251.             if self.src and (self.src == "any" or self.src == "::/0"):
  252.                 self.src = "0.0.0.0/0"
  253.  
  254.     def set_v6(self, v6):
  255.         '''Sets whether this is ipv6 rule, and adjusts src and dst
  256.            accordingly.
  257.         '''
  258.         self.v6 = v6
  259.         self._fix_anywhere()
  260.  
  261.     def set_src(self, addr):
  262.         '''Sets source address of rule'''
  263.         tmp = addr.lower()
  264.  
  265.         if tmp != "any" and not ufw.util.valid_address(tmp, "any"):
  266.             err_msg = _("Bad source address")
  267.             raise UFWError(err_msg)
  268.         self.src = tmp
  269.         self._fix_anywhere()
  270.  
  271.     def set_dst(self, addr):
  272.         '''Sets destination address of rule'''
  273.         tmp = addr.lower()
  274.  
  275.         if tmp != "any" and not ufw.util.valid_address(tmp, "any"):
  276.             err_msg = _("Bad destination address")
  277.             raise UFWError(err_msg)
  278.         self.dst = tmp
  279.         self._fix_anywhere()
  280.  
  281.     def set_interface(self, type, name):
  282.         '''Sets an interface for rule'''
  283.         if type != "in" and type != "out":
  284.             err_msg = _("Bad interface type")
  285.             raise UFWError(err_msg)
  286.  
  287.         if not re.match(r'^[a-zA-Z][a-zA-Z0-9:]*[0-9]', str(name)):
  288.             err_msg = _("Bad interface name")
  289.             raise UFWError(err_msg)
  290.  
  291.         if ':' in str(name):
  292.             err_msg = _("Bad interface name: can't use interface aliases")
  293.             raise UFWError(err_msg)
  294.  
  295.         if type == "in":
  296.             self.interface_in = name
  297.         else:
  298.             self.interface_out = name
  299.  
  300.     def set_position(self, num):
  301.         '''Sets the position of the rule'''
  302.         if not re.match(r'^[0-9]+', str(num)):
  303.             err_msg = _("Insert position '%s' is not a valid position") % (num)
  304.             raise UFWError(err_msg)
  305.         self.position = int(num)
  306.  
  307.     def set_logtype(self, logtype):
  308.         '''Sets logtype of the rule'''
  309.         if logtype.lower() == "log" or logtype.lower() == "log-all" or \
  310.            logtype == "":
  311.             self.logtype = logtype.lower()
  312.         else:
  313.             err_msg = _("Invalid log type '%s'") % (logtype)
  314.             raise UFWError(err_msg)
  315.  
  316.     def set_direction(self, direction):
  317.         '''Sets direction of the rule'''
  318.         if direction == "in" or direction == "out":
  319.             self.direction = direction
  320.         else:
  321.             err_msg = _("Unsupported direction '%s'") % (direction)
  322.             raise UFWError(err_msg)
  323.  
  324.     def normalize(self):
  325.         '''Normalize src and dst to standard form'''
  326.         changed = False
  327.         if self.src:
  328.             try:
  329.                 (self.src, changed) = ufw.util.normalize_address(self.src, \
  330.                                                                  self.v6)
  331.             except Exception:
  332.                 raise
  333.                 err_msg = _("Could not normalize source address")
  334.                 raise UFWError(err_msg)
  335.         if changed:
  336.             self.updated = changed
  337.  
  338.         if self.dst:
  339.             try:
  340.                 (self.dst, changed) = ufw.util.normalize_address(self.dst, \
  341.                                                                    self.v6)
  342.             except Exception:
  343.                 err_msg = _("Could not normalize destination address")
  344.                 raise UFWError(err_msg)
  345.  
  346.         if self.dport:
  347.             ports = self.dport.split(',')
  348.             ufw.util.human_sort(ports)
  349.             self.dport = ','.join(ports)
  350.  
  351.         if self.sport:
  352.             ports = self.sport.split(',')
  353.             ufw.util.human_sort(ports)
  354.             self.sport = ','.join(ports)
  355.  
  356.         if changed:
  357.             self.updated = changed
  358.  
  359.     def match(x, y):
  360.         '''Check if rules match
  361.         Return codes:
  362.           0  match
  363.           1  no match
  364.          -1  match all but action
  365.         '''
  366.         if not x or not y:
  367.             raise ValueError()
  368.  
  369.         dbg_msg = _("No match")
  370.         if x.dport != y.dport:
  371.             debug(dbg_msg)
  372.             return 1
  373.         if x.sport != y.sport:
  374.             debug(dbg_msg)
  375.             return 1
  376.         if x.protocol != y.protocol:
  377.             debug(dbg_msg)
  378.             return 1
  379.         if x.src != y.src:
  380.             debug(dbg_msg)
  381.             return 1
  382.         if x.dst != y.dst:
  383.             debug(dbg_msg)
  384.             return 1
  385.         if x.v6 != y.v6:
  386.             debug(dbg_msg)
  387.             return 1
  388.         if x.dapp != y.dapp:
  389.             debug(dbg_msg)
  390.             return 1
  391.         if x.sapp != y.sapp:
  392.             debug(dbg_msg)
  393.             return 1
  394.         if x.interface_in != y.interface_in:
  395.             return 1
  396.         if x.interface_out != y.interface_out:
  397.             return 1
  398.         if x.direction != y.direction:
  399.             return 1
  400.         if x.action == y.action and x.logtype == y.logtype:
  401.             dbg_msg = _("Found exact match")
  402.             debug(dbg_msg)
  403.             return 0
  404.         dbg_msg = _("Found non-action/non-logtype match " \
  405.                     "(%(xa)s/%(ya)s %(xl)s/%(yl)s)") % \
  406.                     ({'xa': x.action, 'ya': y.action, \
  407.                       'xl': x.logtype, 'yl': y.logtype})
  408.         debug(dbg_msg)
  409.         return -1
  410.  
  411.     def get_app_tuple(self):
  412.         '''Returns a tuple to identify an app rule. Tuple is:
  413.              dapp dst sapp src
  414.            or
  415.              dport dst sapp src
  416.            or
  417.              dapp dst sport src
  418.  
  419.            All of these might have in_eth0 out_eth0 (or similar) if an
  420.            interface is also defined.
  421.         '''
  422.         tuple = ""
  423.         if self.dapp != "" or self.sapp != "":
  424.             tuple = "%s %s %s %s" % (self.dapp, self.dst, self.sapp, self.src)
  425.             if self.dapp == "":
  426.                 tuple = "%s %s %s %s" % (self.dport, self.dst, self.sapp, \
  427.                                          self.src)
  428.             if self.sapp == "":
  429.                 tuple = "%s %s %s %s" % (self.dapp, self.dst, self.sport, \
  430.                                          self.src)
  431.  
  432.             # add interfaces to the end, if they exist
  433.             if self.interface_in != "":
  434.                 tuple += " in_%s" % (self.interface_in)
  435.             if self.interface_out != "":
  436.                 tuple += " out_%s" % (self.interface_out)
  437.  
  438.         return tuple
  439.  
  440.